﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;
using System.Windows;
using Microsoft.Surface.Presentation.Controls;

namespace Master
{
    /*
     * The networkmanager is used to manage every open connection.
     * @Creator T.T. Dusseldorp
     * @Changes made by:
     *      - A.J. Janssen
     *      - J.J. Burgers
     */
    public class NetworkManager
    {
        public static readonly NetworkManager INSTANCE = new NetworkManager();
        private TcpListener tcpListener;
        private Thread listenThread;
        private Dictionary<SpawnPoint, Server> servers;
        private TcpClient client;
        private List<Client> clients;

        /***************
         * CONSTRUCTOR *
         ***************/
        private NetworkManager()
        {
            servers = new Dictionary<SpawnPoint, Server>();
            this.tcpListener = new TcpListener(IPAddress.Any, 1440);
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            this.listenThread.Start();
            clients = new List<Client>();
        }
        
        /********************
         * PUBLIC FUNCTIONS *
         ********************/

        /*
         * Function used to add a client to the clientlist
         */
        public void addClient(Client client)
        {
            clients.Add(client);
        }

        /*
         * Function used to handle the accept from a server
         */
        public void acceptConnection(SpawnPoint spawnPoint)
        {
            //create a new server that will handle connection with the new client
            Server server = new Server(spawnPoint, client);
            //adds spawnpoint to the server dictionary
            servers.Add(spawnPoint, server);
            //sets all pending spawnpoints to default state
            setSpawnPointsState(SpawnpointState.PENDING, SpawnpointState.DEFAULT);
        }

        /*
         * Function used to close a connection
         */
        public void closeConnection(SpawnPoint spawnPoint)
        {
            Server server;
            Boolean serverFound = servers.TryGetValue(spawnPoint, out server);
            if (serverFound)
            {
                server.closeConnection();
                servers.Remove(spawnPoint);
            }
            else
            {
                spawnPoint.setState(SpawnpointState.DEFAULT);
            }
        }

        /*
         * Function used to handle the refuse from a server
         */
        public void refuseConnection()
        {
            setSpawnPointsState(SpawnpointState.PENDING, SpawnpointState.DEFAULT);
            client.Close();
        }

        /*
         * Function used to get the instance from the network manager.
         */
        public static NetworkManager getInstance()
        {
            return INSTANCE;
        }

        public void hookupSurfaceWindowEvent(SurfaceWindow window)
        {
            window.Closed += stop;

        }
        
        /*
         * Function used to stop the network manager
         */
        public void stop(object sender, EventArgs e)
        {
            this.listenThread.Abort();
            this.tcpListener.Stop();
            stopServers();
            stopClients();
            
        }

        /*
         * Function used to stop all servers
         */
        private void stopServers()
        {
            foreach (Server server in servers.Values)
            {
                server.closeConnection();
            }
        }

        /*********************
         * PRIVATE FUNCTIONS *
         *********************/
        private void ListenForClients()
        {
            this.tcpListener.Start();
            while (true)
            {
                //blocks until a client has connected to the server
                client = this.tcpListener.AcceptTcpClient();

                //Set all spawnpoints that are not connected to pending state
                setSpawnPointsState(SpawnpointState.DEFAULT, SpawnpointState.PENDING);
            }
        }

        private void stopClients()
        {
            foreach (Client client in clients)
            {
                client.Close();
            }
        }

        private void setSpawnPointsState(SpawnpointState stateEqual, SpawnpointState newState)
        {
            Map.INSTANCE.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
            new Action(delegate()
            {
                List<SpawnPoint> spawnPoints = Map.INSTANCE.getSpawnPoints();
                foreach (SpawnPoint sp in spawnPoints)
                {
                    if (sp.getState() == stateEqual)
                    {
                        sp.setState(newState);
                    }
                }
            }));
        }
    }
}
